package service

import (
	"context"
	"fmt"
	"github.com/go-rod/rod"
	"github.com/go-rod/rod/lib/proto"
	"github.com/gocolly/colly"
	"strconv"
	"time"

	"github.com/gogf/gf/v2/text/gstr"

	"github.com/beherochuling/beesoft_api/app/dao"
	"github.com/beherochuling/beesoft_api/app/helper/helper"
	"github.com/beherochuling/beesoft_api/app/model"
	"github.com/beherochuling/beesoft_api/app/model/entity"
)

type ipService struct {
	userAgent string
	cookie    string
}

func (r *ipService) CrawlCountries(ctx context.Context, slug string) {
	var items []entity.Constants

	err := dao.Constants.Ctx(ctx).
		Where(dao.Constants.Columns().Type, model.ConstTypeIpCountry).
		Scan(&items)
	if err != nil {
		panic(err)
	}

	for _, item := range items {
		fmt.Printf("crawl %s %s\n", slug, item.Slug)

		switch slug {
		case "rod":
			r.CrawlCountryRod(ctx, item.Slug)
		case "colly":
			fallthrough
		default:
			r.CrawlCountryColly(ctx, item.Slug)
		}
	}
}
func (r *ipService) CrawlCountryRod(ctx context.Context, slug string) {
	uri := "ipblock.chacuo.net"

	// uri = fmt.Sprintf("http://%s/view/c_%s", uri, slug)
	uri = fmt.Sprintf("http://%s/down/t_txt=c_%s", uri, slug)
	// uri = fmt.Sprintf("http://%s/down/t_file=c_%s", uri, slug)

	page := rod.New().
		MustConnect().
		MustPage(uri)

	page = page.MustSetUserAgent(&proto.NetworkSetUserAgentOverride{
		UserAgent: r.userAgent,
	})

	time.Sleep(time.Second * 3)

	data := page.MustElement("pre").MustText()

	r.StoreCountry(ctx, data, slug)
}
func (r *ipService) CrawlCountryColly(ctx context.Context, slug string) {
	uri := "ipblock.chacuo.net"

	// uri = fmt.Sprintf("http://%s/view/c_%s", uri, slug)
	uri = fmt.Sprintf("http://%s/down/t_txt=c_%s", uri, slug)
	// uri = fmt.Sprintf("http://%s/down/t_file=c_%s", uri, slug)

	page := rod.New().
		MustConnect().
		MustPage(uri)

	page = page.MustSetUserAgent(&proto.NetworkSetUserAgentOverride{
		UserAgent: r.userAgent,
	})

	time.Sleep(time.Second * 3)

	data := page.MustElement("pre").MustText()

	r.StoreCountry(ctx, data, slug)
}
func (r *ipService) StoreCountry(ctx context.Context, data, slug string) {
	var entities []entity.Ips

	items := gstr.Explode("\n", data)
	for _, item := range items {
		values := gstr.Explode("\t", item)
		if len(values) != 4 {
			fmt.Println("invalid data: ", item)
			continue
		}

		count, err := strconv.Atoi(values[3])
		if err != nil {
			panic(err)
		}

		entities = append(entities, entity.Ips{
			Type:    model.ConstTypeIpCountry,
			Slug:    slug,
			StartIp: values[0],
			EndIp:   values[1],
			Net:     values[2],
			Start:   uint(helper.Ipv42Uint32(values[0])),
			End:     uint(helper.Ipv42Uint32(values[1])),
			Count:   uint(count),
		})
	}

	_, err := dao.Ips.Ctx(ctx).Insert(entities)
	if err != nil {
		panic(err)
	}
}

func (r *ipService) CrawlProvinces(ctx context.Context, slug string) {
	var items []entity.Constants

	err := dao.Constants.Ctx(ctx).
		Where(dao.Constants.Columns().Type, model.ConstTypeIpProvince).
		Scan(&items)
	if err != nil {
		panic(err)
	}

	for _, item := range items {
		fmt.Printf("crawl %s %s\n", slug, item.Slug)

		switch slug {
		case "rod":
			r.CrawlProvinceRod(ctx, item.Slug)
		case "colly":
			fallthrough
		default:
			r.CrawlProvinceColly(ctx, item.Slug)
		}
	}
}
func (r *ipService) CrawlProvinceRod(ctx context.Context, slug string) {
	uri := "ips.chacuo.net"

	// uri = fmt.Sprintf("http://%s/view/s_%s", uri, slug)
	uri = fmt.Sprintf("http://%s/down/t_txt=p_%s", uri, slug)
	// uri = fmt.Sprintf("http://%s/down/t_file=p_%s", uri, slug)

	page := rod.New().
		MustConnect().
		MustPage(uri)

	page = page.MustSetUserAgent(&proto.NetworkSetUserAgentOverride{
		UserAgent: r.userAgent,
	})

	time.Sleep(time.Second * 3)

	data := page.MustElement("pre").MustText()

	r.StoreProvince(ctx, data, slug)
}
func (r *ipService) CrawlProvinceColly(ctx context.Context, slug string) {
	uri := "ips.chacuo.net"

	c := colly.NewCollector()

	c.UserAgent = r.userAgent
	err := c.SetCookies(fmt.Sprintf("http://%s", uri), helper.CookieRaw(r.cookie))
	if err != nil {
		panic(err)
	}

	err = c.Limit(&colly.LimitRule{
		DomainRegexp: uri,
		Delay:        1 * time.Second,
	})
	if err != nil {
		panic(err)
	}

	c.OnHTML("pre", func(t *colly.HTMLElement) {
		fmt.Println(t.Text)

		r.StoreProvince(ctx, t.Text, slug)
	})

	// uri = fmt.Sprintf("http://%s/view/s_%s", uri, slug)
	uri = fmt.Sprintf("http://%s/down/t_txt=p_%s", uri, slug)
	// uri = fmt.Sprintf("http://%s/down/t_file=p_%s", uri, slug)
	helper.PanicIgnore(context.Background(), func(ctx context.Context) {
		err = c.Visit(uri)
		if err != nil {
			panic(err)
		}
	}, func(err error) {
		// if err.Error() == "Service Unavailable" {} // todo
	})
}
func (r *ipService) StoreProvince(ctx context.Context, data, slug string) {
	var entities []entity.Ips

	items := gstr.Explode("\n", data)
	for _, item := range items {
		values := gstr.Explode("\t", item)
		if len(values) != 3 {
			fmt.Println("invalid data: ", item)
			continue
		}

		count, err := strconv.Atoi(values[2])
		if err != nil {
			panic(err)
		}

		entities = append(entities, entity.Ips{
			Type:    model.ConstTypeIpProvince,
			Slug:    slug,
			StartIp: values[0],
			EndIp:   values[1],
			Net:     "",
			Start:   uint(helper.Ipv42Uint32(values[0])),
			End:     uint(helper.Ipv42Uint32(values[1])),
			Count:   uint(count),
		})
	}

	_, err := dao.Ips.Ctx(ctx).Insert(entities)
	if err != nil {
		panic(err)
	}
}

// ## IP 获取
// http://ipblock.chacuo.net 国家 IP
// http://ipblock.chacuo.net/view/c_AO
// http://ipblock.chacuo.net/down/t_txt=c_CN txt
// http://ipblock.chacuo.net/down/t_file=c_AO file

// http://ips.chacuo.net 省市 IP
// http://ips.chacuo.net/view/s_YN
// http://ips.chacuo.net/down/t_txt=p_YN txt
// http://ips.chacuo.net/down/t_file=p_YN file
